package drds.configuration.db.xml_mapping.sql_execute;

import drds.configuration.db.datasource.DataSourceContext;
import drds.configuration.db.datasource.DataSourceIdToDataSourceConfigMapContext;
import drds.configuration.db.xml_mapping.configuration.Configuration;
import drds.configuration.db.xml_mapping.dynamic_tag.sql_template.Bind;
import drds.configuration.db.xml_mapping.sql_execute.primary_key_generator.AutoSetModePrimaryKeyGenerator;
import drds.configuration.db.xml_mapping.sql_execute.primary_key_generator.PrimaryKeyGenerator;
import drds.tools.Constants;
import drds.tools.Log;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class MapperExecutionContextImpl implements MapperExecutionContext {

    private final Configuration configuration;

    public MapperExecutionContextImpl(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }

    /***
     * 下面的方法用于底层具体调用
     * @return
     * @throws SQLException
     */

    protected Connection getConnection() throws SQLException {
//        try {
////            Class.forName("com.mysql.jdbc.Driver");   //com.mysql.jdbc为驱动所在包
////        } catch (ClassNotFoundException e) {
////            e.printStackTrace();
////        }
////        String url = "jdbc:mysql://localhost:3306/drds_home?characterEncoding=utf-8&serverTimezone=UTC";
////        String username = "root";
////        String password = "root";
////        Connection connection = DriverManager.getConnection(url, username, password);
////        return connection;
        Connection connection = null;
        DataSourceContext dataSourceContext = DataSourceIdToDataSourceConfigMapContext.getDataSourceIdToDataSourceConfigMap().get(configuration.getMapperDatabaseId());
        connection = dataSourceContext.getConnection();
        return connection;
    }

    private Statement prepareStatement(StatementHandler statementHandler) throws SQLException {
        Connection connection = getConnection();
        Statement statement = statementHandler.prepareStatement(connection);
        statementHandler.parameterize(statement);
        return statement;
    }

    private Statement prepareStatementForBatchInsert(StatementHandler statementHandler, List<Map<String, Object>> parameterMapList) throws SQLException {
        Connection connection = getConnection();
        Statement statement = statementHandler.prepareStatement(connection);
        statementHandler.parameterizeForBatchInsert(statement, parameterMapList);
        return statement;
    }

    //
    @Override
    public int insert(String mapperId) {
        return insert(mapperId, new HashMap<String, Object>());
    }

    @Override
    public int insert(String mapperId, Map<String, Object> parameterMap) {
        return update(mapperId, parameterMap);
    }

    @Override
    public int batchInsert(String mapperId, List<Map<String, Object>> parameterMapList) {
        try {
            Mapper mapper = configuration.getMapper(mapperId);
            return executeBatchInsert(mapper, parameterMapList);
        } catch (Exception e) {
            return 0;
        }
    }

    public int executeBatchInsert(Mapper mapper, List<Map<String, Object>> parameterMapList) {
        Bind bind = mapper.getSqlTemplate().bindWith(new HashMap<>());
        final StatementHandler statementHandler = new StatementHandlerImpl(mapper, bind);
        Statement statement = null;
        try {
            statement = prepareStatementForBatchInsert(statementHandler, parameterMapList);
            statement.executeBatch();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        //
        PrimaryKeyGenerator primaryKeyGenerator = mapper.getPrimaryKeyGenerator();
        if (AutoSetModePrimaryKeyGenerator.class.equals(primaryKeyGenerator.getClass())) {
            AutoSetModePrimaryKeyGenerator autoSetModePrimaryKeyGenerator = (AutoSetModePrimaryKeyGenerator) primaryKeyGenerator;
            autoSetModePrimaryKeyGenerator.batchInsert(mapper, statement, parameterMapList);
        }
        statementHandler.closeStatement(statement);
        return parameterMapList.size();
    }

    @Override
    public int update(String mapperId) {
        return update(mapperId, new HashMap<String, Object>());
    }

    @Override
    public int update(String mapperId, Map<String, Object> parameterMap) {
        try {
            Mapper mapper = configuration.getMapper(mapperId);
            return executeUpdate(mapper, parameterMap);
        } catch (Exception e) {
            return 0;
        }
    }

    public int executeUpdate(Mapper mapper, Map<String, Object> parameter) throws SQLException {
        Statement statement = null;
        StatementHandler statementHandler = null;
        try {
            Bind bind = mapper.getSqlTemplate().bindWith(parameter);
            statementHandler = new StatementHandlerImpl(mapper, bind);
            statement = prepareStatement(statementHandler);
            return statementHandler.update(statement);
        } catch (Exception e) {
            if (Constants.developMode) {
                e.printStackTrace();
            }
            Log.error(log, Thread.currentThread(), "", e);
            throw new RuntimeException(e);
        } finally {
            if (statementHandler != null) {
                statementHandler.closeStatement(statement);
            }
        }
    }

    @Override
    public int delete(String mapperId) {
        return update(mapperId, new HashMap<String, Object>());
    }

    @Override
    public int delete(String mapperId, Map<String, Object> parameterMap) {
        return update(mapperId, parameterMap);
    }

    //
    @Override
    public List<Map<String, Object>> selectMapList(String mapperId, Map<String, Object> parameterMap) {
        Mapper mapper = configuration.getMapper(mapperId);
        try {
            Bind bind = mapper.getSqlTemplate().bindWith(parameterMap);
            return executeQuery(mapper, bind);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    public List<Map<String, Object>> executeQuery(Mapper mapper, Bind bind) throws SQLException {
        Statement statement = null;
        StatementHandler statementHandler = new StatementHandlerImpl(mapper, bind);
        try {
            statement = prepareStatement(statementHandler);
            List<Map<String, Object>> mapList = statementHandler.query(statement);
            return mapList;
        } finally {
            statementHandler.closeStatement(statement);
        }
    }
}
